#include <Windows.h>
#include <d3d12.h>
#include <dxgi1_6.h>
#include <chrono>
#include <thread>
#include <iostream>
#include <deque>

class DX12Timer {
public:
    using Clock = std::chrono::high_resolution_clock;
    using TimePoint = Clock::time_point;
    
private:
    TimePoint m_startTime;
    TimePoint m_lastFrameTime;
    TimePoint m_currentFrameTime;
    
    double m_deltaTime = 0.0;
    double m_totalTime = 0.0;
    double m_fps = 0.0;
    
    // Frame rate limiting
    double m_targetFPS = 60.0;
    double m_targetFrameTime = 1.0 / 60.0; // 16.67ms
    
    // Frame time averaging for smooth FPS calculation
    std::deque<double> m_frameTimeHistory;
    static constexpr size_t MAX_FRAME_HISTORY = 60;
    
    // Performance monitoring
    LARGE_INTEGER m_frequency;
    bool m_useQPC = false; // Query Performance Counter
    
public:
    DX12Timer() {
        m_startTime = Clock::now();
        m_lastFrameTime = m_startTime;
        m_currentFrameTime = m_startTime;
        
        // Initialize high-precision timer if available
        if (QueryPerformanceFrequency(&m_frequency)) {
            m_useQPC = true;
        }
    }
    
    void tick() {
        m_lastFrameTime = m_currentFrameTime;
        m_currentFrameTime = Clock::now();
        
        // Calculate delta time in seconds
        auto duration = m_currentFrameTime - m_lastFrameTime;
        m_deltaTime = std::chrono::duration<double>(duration).count();
        
        // Calculate total elapsed time
        auto totalDuration = m_currentFrameTime - m_startTime;
        m_totalTime = std::chrono::duration<double>(totalDuration).count();
        
        // Update frame time history for FPS calculation
        m_frameTimeHistory.push_back(m_deltaTime);
        if (m_frameTimeHistory.size() > MAX_FRAME_HISTORY) {
            m_frameTimeHistory.pop_front();
        }
        
        // Calculate average FPS
        if (!m_frameTimeHistory.empty()) {
            double avgFrameTime = 0.0;
            for (double frameTime : m_frameTimeHistory) {
                avgFrameTime += frameTime;
            }
            avgFrameTime /= m_frameTimeHistory.size();
            m_fps = avgFrameTime > 0.0 ? 1.0 / avgFrameTime : 0.0;
        }
    }
    
    // Limit frame rate to target FPS
    void limitFrameRate() {
        if (m_targetFPS <= 0.0) return;
        
        double elapsed = m_deltaTime;
        if (elapsed < m_targetFrameTime) {
            double sleepTime = m_targetFrameTime - elapsed;
            
            // Use high-precision sleep for better accuracy
            if (sleepTime > 0.001) { // Only sleep if > 1ms
                auto sleepDuration = std::chrono::duration<double>(sleepTime);
                std::this_thread::sleep_for(sleepDuration);
            }
        }
    }
    
    // Getters
    double getDeltaTime() const { return m_deltaTime; }
    double getTotalTime() const { return m_totalTime; }
    double getFPS() const { return m_fps; }
    double getTargetFPS() const { return m_targetFPS; }
    
    // Setters
    void setTargetFPS(double fps) {
        m_targetFPS = fps;
        m_targetFrameTime = fps > 0.0 ? 1.0 / fps : 0.0;
    }
    
    // Get time in milliseconds for easier reading
    double getDeltaTimeMS() const { return m_deltaTime * 1000.0; }
    double getTotalTimeMS() const { return m_totalTime * 1000.0; }
};

class DX12RenderLoop {
private:
    DX12Timer m_timer;
    bool m_vsyncEnabled = true;
    bool m_running = true;
    
    // Performance monitoring
    double m_frameTimeAccumulator = 0.0;
    int m_frameCount = 0;
    static constexpr double PERF_UPDATE_INTERVAL = 1.0; // Update every second
    
public:
    DX12RenderLoop() = default;
    
    // Main render loop
    void run() {
        while (m_running) {
            m_timer.tick();
            
            // Handle Windows messages
            if (!processMessages()) {
                break;
            }
            
            // Update game logic
            update(m_timer.getDeltaTime(), m_timer.getTotalTime());
            
            // Render frame
            render();
            
            // Present frame
            present();
            
            // Frame rate limiting (if not using VSync)
            if (!m_vsyncEnabled) {
                m_timer.limitFrameRate();
            }
            
            // Update performance statistics
            updatePerformanceStats();
        }
    }
    
    void stop() { m_running = false; }
    
    void setVSync(bool enabled) { m_vsyncEnabled = enabled; }
    void setTargetFPS(double fps) { m_timer.setTargetFPS(fps); }
    
private:
    bool processMessages() {
        MSG msg = {};
        while (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) {
            if (msg.message == WM_QUIT) {
                return false;
            }
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        return true;
    }
    
    void update(double deltaTime, double totalTime) {
        // Update your game logic here
        // Example: Update animations, physics, input, etc.
        
        // Clamp delta time to prevent large jumps
        deltaTime = std::min(deltaTime, 1.0 / 30.0); // Max 30 FPS equivalent
        
        // Update your scene/objects
        updateScene(deltaTime, totalTime);
    }
    
    void render() {
        // Your DX12 rendering code here
        // Example structure:
        
        // 1. Begin frame
        beginFrame();
        
        // 2. Set up command list
        recordCommandList();
        
        // 3. Execute commands
        executeCommandList();
        
        // 4. End frame
        endFrame();
    }
    
    void present() {
        // Present the frame
        UINT syncInterval = m_vsyncEnabled ? 1 : 0;
        UINT presentFlags = 0;
        
        // If tearing is supported and VSync is off, allow tearing
        if (!m_vsyncEnabled && supportsTearing()) {
            presentFlags = DXGI_PRESENT_ALLOW_TEARING;
        }
        
        // Present the frame
        // m_swapChain->Present(syncInterval, presentFlags);
    }
    
    void updatePerformanceStats() {
        m_frameTimeAccumulator += m_timer.getDeltaTime();
        m_frameCount++;
        
        if (m_frameTimeAccumulator >= PERF_UPDATE_INTERVAL) {
            double avgFPS = m_frameCount / m_frameTimeAccumulator;
            double avgFrameTime = (m_frameTimeAccumulator / m_frameCount) * 1000.0;
            
            // Update window title or console output
            updatePerformanceDisplay(avgFPS, avgFrameTime);
            
            // Reset counters
            m_frameTimeAccumulator = 0.0;
            m_frameCount = 0;
        }
    }
    
    void updatePerformanceDisplay(double fps, double frameTimeMS) {
        // Update window title with performance info
        std::wstring title = L"DX12 App - FPS: " + std::to_wstring((int)fps) + 
                            L" - Frame Time: " + std::to_wstring(frameTimeMS) + L"ms";
        // SetWindowText(m_hwnd, title.c_str());
        
        // Or output to console
        std::wcout << title << std::endl;
    }
    
    // Placeholder functions - implement with your actual DX12 code
    void updateScene(double deltaTime, double totalTime) {
        // Update your constant buffers with time values
        // Example:
        /*
        Constants constants;
        constants.Time = DirectX::XMFLOAT3(
            static_cast<float>(totalTime),
            static_cast<float>(deltaTime),
            static_cast<float>(m_timer.getFPS())
        );
        // Update constant buffer...
        */
    }
    
    void beginFrame() {
        // Reset command allocator, begin command list, etc.
    }
    
    void recordCommandList() {
        // Record all your rendering commands
    }
    
    void executeCommandList() {
        // Close and execute command list
    }
    
    void endFrame() {
        // Signal fence, wait for GPU if needed
    }
    
    bool supportsTearing() {
        // Check if DXGI supports variable refresh rate/tearing
        return false; // Implement actual check
    }
};

// Alternative: Fixed timestep with interpolation
class FixedTimestepLoop {
private:
    DX12Timer m_timer;
    
    static constexpr double FIXED_TIMESTEP = 1.0 / 60.0; // 60 Hz physics
    static constexpr double MAX_FRAME_TIME = 0.25; // 4 FPS minimum
    
    double m_accumulator = 0.0;
    double m_currentTime = 0.0;
    
public:
    void run() {
        m_timer.tick();
        double newTime = m_timer.getTotalTime();
        double frameTime = newTime - m_currentTime;
        
        // Prevent spiral of death
        frameTime = std::min(frameTime, MAX_FRAME_TIME);
        
        m_currentTime = newTime;
        m_accumulator += frameTime;
        
        // Fixed timestep updates
        while (m_accumulator >= FIXED_TIMESTEP) {
            updatePhysics(FIXED_TIMESTEP); // Always consistent timestep
            m_accumulator -= FIXED_TIMESTEP;
        }
        
        // Interpolation factor for smooth rendering
        double interpolation = m_accumulator / FIXED_TIMESTEP;
        
        // Render with interpolation
        render(interpolation);
        present();
    }
    
private:
    void updatePhysics(double dt) {
        // Update physics/game logic with fixed timestep
    }
    
    void render(double interpolation) {
        // Render with interpolated positions for smooth visuals
    }
    
    void present() {
        // Present frame
    }
};

// Example usage
int main() {
    try {
        // Initialize DX12...
        
        DX12RenderLoop renderLoop;
        renderLoop.setTargetFPS(60.0);
        renderLoop.setVSync(false); // Use manual frame limiting
        
        // Run the main loop
        renderLoop.run();
        
        // Cleanup...
        
    } catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
        return -1;
    }
    
    return 0;
}

// Utility: High-precision timer for profiling specific operations
class ScopedTimer {
private:
    std::chrono::high_resolution_clock::time_point m_start;
    const char* m_name;
    
public:
    ScopedTimer(const char* name) : m_name(name) {
        m_start = std::chrono::high_resolution_clock::now();
    }
    
    ~ScopedTimer() {
        auto end = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration<double, std::milli>(end - m_start);
        std::cout << m_name << ": " << duration.count() << "ms" << std::endl;
    }
};

// Usage: ScopedTimer timer("GPU Command Recording");